package com.cy.ragbase.service;


import com.cy.ragbase.dto.DocumentProcessingTask;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
@Slf4j
public class RedisQueueService {

    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    @Value("${app.redis.queue.document-processing}")
    private String documentProcessingQueue;

    @Value("${app.redis.queue.max-retries}")
    private int maxRetries;

    @Value("${app.redis.queue.retry-delay}")
    private long retryDelay;

    /**
     * 添加文档处理任务到队列
     */
    public String enqueueDocumentProcessing(String documentId, String filePath, String fileName) {
        try {

            DocumentProcessingTask task = new DocumentProcessingTask();
            task.setTaskId(documentId);
            task.setDocumentId(documentId);
            task.setFilePath(filePath);
            task.setRetryCount(0);
            task.setFileName(fileName);
            task.setTimestamp(System.currentTimeMillis());

            String taskJson = objectMapper.writeValueAsString(task);

            // 使用Redis LIST作为队列
            redisTemplate.opsForList().leftPush(documentProcessingQueue, taskJson);

            // 设置任务状态
            setTaskStatus(documentId, "QUEUED");
            return documentId;

        } catch (Exception e) {
            log.error("文档处理任务入队失败", e);
            throw new RuntimeException("Failed to enqueue task", e);
        }
    }

    /**
     * 从队列中获取待处理任务
     */
    public DocumentProcessingTask dequeueDocumentProcessing(long timeoutSeconds) {
        try {
            // 阻塞式获取任务
            String taskJson = redisTemplate.opsForList()
                    .rightPop(documentProcessingQueue, timeoutSeconds, TimeUnit.SECONDS);

            if (taskJson != null) {
                DocumentProcessingTask task = objectMapper.readValue(taskJson, DocumentProcessingTask.class);
                setTaskStatus(task.getTaskId(), "PROCESSING");
                String fileName = task.getFileName() != null ? task.getFileName() : "未知文件";
                log.info("取出文档处理任务: {} (文件: {})", task.getTaskId(), fileName);
                return task;
            }

            return null;

        } catch (Exception e) {
            log.error("执行文件处理任务失败", e);
            throw new RuntimeException("Failed to dequeue task", e);
        }
    }

    /**
     * 任务处理成功
     */
    public void markTaskCompleted(DocumentProcessingTask task) {
        String taskId = task.getTaskId();

        setTaskStatus(taskId, "COMPLETED");
        removeTaskData(taskId);
    }

    /**
     * 任务处理失败，可能需要重试
     */
    public void markTaskFailed(String taskId, String errorMessage, DocumentProcessingTask originalTask) {
        try {
            if (originalTask.getRetryCount() < maxRetries) {
                // 增加重试次数并重新入队
                originalTask.setRetryCount(originalTask.getRetryCount() + 1);
                originalTask.setTimestamp(System.currentTimeMillis());

                String taskJson = objectMapper.writeValueAsString(originalTask);

                // 延迟重试
                String delayedQueueKey = "delayed:" + documentProcessingQueue;
                redisTemplate.opsForZSet().add(delayedQueueKey, taskJson,
                        System.currentTimeMillis() + retryDelay);

                setTaskStatus(taskId, "RETRY_SCHEDULED");
                setTaskError(taskId, errorMessage);

                log.info("安排第 {} 次重试任务: {}", originalTask.getRetryCount(), taskId);
            } else {
                // 超过最大重试次数，标记为失败
                setTaskStatus(taskId, "FAILED");
                setTaskError(taskId, errorMessage);
                removeTaskData(taskId);

                log.error("任务在 {} 次重试后失败: {}, 错误: {}", maxRetries, taskId, errorMessage);
            }

        } catch (Exception e) {
            log.error("处理任务失败记录失败: {}", taskId, e);
        }
    }

    /**
     * 获取任务状态
     */
    public String getTaskStatus(String taskId) {
        String key = "task:status:" + taskId;
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 设置任务状态
     */
    private void setTaskStatus(String taskId, String status) {
        String key = "task:status:" + taskId;
        redisTemplate.opsForValue().set(key, status, 24, TimeUnit.HOURS);
    }

    /**
     * 设置任务错误信息
     */
    private void setTaskError(String taskId, String errorMessage) {
        String key = "task:error:" + taskId;
        redisTemplate.opsForValue().set(key, errorMessage, 24, TimeUnit.HOURS);
    }

    /**
     * 获取任务错误信息
     */
    public String getTaskError(String taskId) {
        String key = "task:error:" + taskId;
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 清理任务相关数据
     */
    private void removeTaskData(String taskId) {
        redisTemplate.delete("task:status:" + taskId);
        redisTemplate.delete("task:error:" + taskId);
    }

    /**
     * 处理延迟重试任务
     */
    public void processDelayedTasks() {
        try {
            String delayedQueueKey = "delayed:" + documentProcessingQueue;
            long currentTime = System.currentTimeMillis();

            // 获取到期的任务
            var expiredTasks = redisTemplate.opsForZSet()
                    .rangeByScore(delayedQueueKey, 0, currentTime);

            if (expiredTasks != null && !expiredTasks.isEmpty()) {
                for (String taskJson : expiredTasks) {
                    // 移回主队列
                    redisTemplate.opsForList().leftPush(documentProcessingQueue, taskJson);
                    redisTemplate.opsForZSet().remove(delayedQueueKey, taskJson);

                    DocumentProcessingTask task = objectMapper.readValue(taskJson, DocumentProcessingTask.class);
                    setTaskStatus(task.getTaskId(), "QUEUED");

                    log.info("延迟任务重新入队: {}", task.getTaskId());
                }
            }

        } catch (Exception e) {
            log.error("处理延迟任务失败", e);
        }
    }
}